﻿using Microsoft.Extensions.DependencyInjection;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Koala.Pro.Common
{
    public class ServiceInjection
    {
        private static string CoreMatchAssemblies_Pattern;

        private static string ApiCallerMatchAssemblies_Pattern;

        public static void ConfigureRepository(IServiceCollection services, string normalAssemblies_Pattern = "^Koala.Pro")
        {
            CoreMatchAssemblies_Pattern = normalAssemblies_Pattern;
            List<Assembly> source = Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory, "*.dll").Where(NormalMatch).Select(Assembly.LoadFrom)
                .ToList();
            Type baseType = typeof(IDependency);
            List<Type> source2 = (from type in source.SelectMany((Assembly a) => a.DefinedTypes)
                                  select type.AsType() into x
                                  where x != baseType && baseType.IsAssignableFrom(x)
                                  select x).ToList();
            List<Type> implementTypes = source2.Where((Type x) => x.IsClass).ToList();
            List<Type> interfaceTypes = source2.Where((Type x) => x.IsInterface).ToList();
            RegisterService(services, implementTypes, interfaceTypes);
        }

        public static void ConfigureRepositoryApi(IServiceCollection services, string apiCallerAssemblies_Pattern = "^ApiCaller")
        {
            ApiCallerMatchAssemblies_Pattern = apiCallerAssemblies_Pattern;
            string? path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
            List<Assembly> source = Directory.GetFiles(path, "*.dll").Where(ApiMatch).Select(Assembly.LoadFrom)
                .ToList();
            Type baseType = typeof(IDependency);
            List<Type> implementTypes = (from x in (from type in source.SelectMany((Assembly a) => a.DefinedTypes)
                                                    select type.AsType() into x
                                                    where x != baseType && baseType.IsAssignableFrom(x)
                                                    select x).ToList()
                                         where x.IsClass
                                         select x).ToList();
            List<Type> interfaceTypes = (from x in (from type in Directory.GetFiles(path, "*.dll").Where(NormalMatch).Select(Assembly.LoadFrom)
                        .ToList()
                        .SelectMany((Assembly a) => a.DefinedTypes)
                                                    select type.AsType() into x
                                                    where x != baseType && baseType.IsAssignableFrom(x)
                                                    select x).ToList()
                                         where x.IsInterface
                                         select x).ToList();
            RegisterService(services, implementTypes, interfaceTypes);
        }

        public static void ConfigureRepositoryAuto(IServiceCollection services, string normalAssemblies_Pattern = "^SugarProjectCore", string apiCallerAssemblies_Pattern = "^ApiCaller")
        {
            CoreMatchAssemblies_Pattern = normalAssemblies_Pattern;
            ApiCallerMatchAssemblies_Pattern = apiCallerAssemblies_Pattern;
            string text = new AppConfig().AppConfigGet("CallerType");
            if (!string.IsNullOrWhiteSpace(text) && text.Equals("api", StringComparison.OrdinalIgnoreCase))
            {
                ConfigureRepositoryApi(services, apiCallerAssemblies_Pattern);
            }
            else
            {
                ConfigureRepository(services, normalAssemblies_Pattern);
            }
        }

        private static void RegisterService(IServiceCollection services, List<Type> implementTypes, List<Type> interfaceTypes)
        {
            foreach (Type implementType in implementTypes)
            {
                if (typeof(IScopedDependency).IsAssignableFrom(implementType))
                {
                    Type type = interfaceTypes.FirstOrDefault((Type x) => x.IsAssignableFrom(implementType));
                    if (type != null)
                    {
                        services.AddScoped(type, implementType);
                    }
                }
                else if (typeof(ISingletonDependency).IsAssignableFrom(implementType))
                {
                    Type type2 = interfaceTypes.FirstOrDefault((Type x) => x.IsAssignableFrom(implementType));
                    if (type2 != null)
                    {
                        services.AddSingleton(type2, implementType);
                    }
                }
                else
                {
                    Type type3 = interfaceTypes.FirstOrDefault((Type x) => x.IsAssignableFrom(implementType));
                    if (type3 != null)
                    {
                        services.AddTransient(type3, implementType);
                    }
                }
            }
        }

        private static bool NormalMatch(string assemblyName)
        {
            if (string.IsNullOrEmpty(CoreMatchAssemblies_Pattern))
            {
                throw new ArgumentNullException("普通模式下的程序集匹配字符串不能为空");
            }
            assemblyName = Path.GetFileName(assemblyName);
            return Regex.IsMatch(assemblyName, CoreMatchAssemblies_Pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
        }

        private static bool ApiMatch(string assemblyName)
        {
            if (string.IsNullOrEmpty(ApiCallerMatchAssemblies_Pattern))
            {
                throw new ArgumentNullException("ApiCaller模式下的程序集匹配字符串不能为空");
            }
            assemblyName = Path.GetFileName(assemblyName);
            return Regex.IsMatch(assemblyName, ApiCallerMatchAssemblies_Pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
        }
    }
}
